import os
from datetime import datetime
from typing import Dict

import allure
import pytest
from _pytest.fixtures import FixtureRequest
from playwright.sync_api import BrowserType
from slugify import slugify

from config import sys_path
from config.sys_path import HAR_DIR, AUTH_DIR, DATAS_DIR
from sample.common.clear_output import clear_output
from sample.common.my_log import my_log
from sample.common.read_config import conf
from sample.util.excel_util import ExcelUtil

test_info = {}  # 用于存储当前测试用例相关信息的全局变量


@pytest.fixture(autouse=True)
def set_test_info(request: FixtureRequest):
    """获取当前用例所属类、目录、方法名、用例名称等相关信息,并存入全局变量以供使用"""
    global test_info
    node_id = request.node.nodeid
    test_info['case_name'] = request.node.name
    test_info['method_name'] = request.node.name.split('[')[0]
    test_info['class_name'] = node_id.split('::')[1]
    test_info['directory'] = node_id.split('/')[2]


@pytest.fixture(scope="session")
def browser_type_launch_args(browser_type_launch_args):
    """重写pytest_playwright夹具browser_type_launch_args，
        增加browser自定义启动参数"""
    return {
        **browser_type_launch_args,
        "args": ["--start-maximized"],
        "devtools": False,
    }


@pytest.fixture
def browser_context_args(browser_context_args):
    """重写pytest_playwright夹具browser_context_args，
    增加context自定义启动参数"""
    now_time = datetime.now().strftime('%Y%m%d_%H')
    # 根据测试用例所在目录，设置context的base_url
    global test_info
    directory = test_info.get('directory')
    # base_url = conf.get('base_url', directory)
    # my_log.info(f'当前使用的base_url：【{base_url}】')
    # 根据测试用例所在目录，获取存储cookie等信息的storage_json文件路径
    if not os.path.exists(AUTH_DIR):
        os.makedirs(AUTH_DIR)
    storage_json = os.path.join(AUTH_DIR, f'{directory}.json')
    if not os.path.exists(storage_json) or os.path.getsize(storage_json) == 0:
        my_log.info(f'文件【{storage_json}】为空')
        storage_json = None
    my_log.info(f'当前使用的storage_state：【{storage_json}】')
    return {
        **browser_context_args,
        # 设置浏览器尺寸（browser参数中已开启全屏，不需额外设置尺寸）
        # "viewport": {
        #     "width": 1920,
        #     "height": 1080,
        # },
        "no_viewport": True,  # 与以上设置浏览器尺寸的参数二选一。
        # "base_url": base_url,  # 设置后，在切换页面时，只需传页面地址即可(已被BasePage中的__init_subclass__方法取代)
        "storage_state": storage_json,  # 加载本地cookie，从而避免每个用例都需要重新登陆
        "record_har_path": f"{os.path.join(HAR_DIR, now_time, test_info.get('case_name'))}.har",  # 设置保存har的路径（接口请求记录）
        # 设置用例录屏保存路径（开启追溯的情况下，不需要额外保存录屏）
        # "record_video_dir": f"{os.path.join(VIDEO_DIR, now_time, test_info.get('case_name'))}",
        "record_video_size": {"width": 1920, "height": 1080},  # 设置用例录屏尺寸
        "locale": "zh-CN",
    }


@pytest.fixture(scope='session')
def browser(browser):
    """重写pytest_playwright的夹具browser，加分隔日志
    (browser、context、page这些fixture其实可以不必重写，这里单纯只是为了加个日志分隔标记，以此标记一次自动化测试的执行)
    """
    clear_output()  # 项目启动时，先清理旧的测试输出文件
    my_log.info('\n-------------------↓↓↓ browser启动 ↓↓↓-------------------')
    yield browser
    my_log.info('\n-------------------↑↑↑ browser关闭 ↑↑↑-------------------')


@pytest.fixture()
def persistent_context(
        browser_type: BrowserType,
        browser_type_launch_args: Dict,
        browser_context_args: Dict
):
    """持久化context（相当于 非无痕模式 启动浏览器），目前暂无场景需要使用，可忽略"""
    global test_info
    my_log.info(f"获取user_data/{test_info.get('test_name')}")
    context = browser_type.launch_persistent_context(f"./user_data/{test_info.get('test_name')}", **{
        **browser_type_launch_args,
        **browser_context_args,
    })
    # 持久化的context会自动开启一个page，我们需要关闭它,否则后面会有两个page
    pages = context.pages
    pages[0].close()
    yield context
    context.close()


@pytest.fixture(autouse=True)
def add_video_to_allure(request: pytest.FixtureRequest):
    """当测试用例存在录屏时，将对应录屏放入allure报告中
    （在allure报告对应用例的Tear down>>add_video_to_allure目录下可查看录屏）
    """
    yield
    trace_path = os.path.join(sys_path.TRACE_VIEWER_DIR, slugify(request.node.nodeid))
    global test_info
    if os.path.exists(trace_path):
        my_log.info(f'用例【{test_info.get("case_name")}】追溯文件所在路径：【{trace_path}】')
        for root, dirs, files in os.walk(trace_path):
            for file in files:
                if '.webm' in file:
                    with open(os.path.join(root, file), 'rb') as f:
                        video = f.read()
                        allure.attach(video, '用例录屏', allure.attachment_type.WEBM)
    my_log.info(
        f'\n=======================↑↑↑ 测试用例【{test_info.get("case_name")}】分隔符 ↑↑↑=======================\n')


@pytest.fixture()
def get_test_excel():
    """根据配置文件config.ini中配置的测试数据文件路径，读取Excel"""
    global test_info
    directory = test_info.get('directory')
    file_path = os.path.join(DATAS_DIR, conf.get('test_data_file', directory))
    my_log.info(f'测试数据文件名为：【{file_path}】')
    excel = ExcelUtil(file_path)
    return excel


@pytest.fixture()
def get_test_datas(get_test_excel, request):
    """
    根据当前测试用例名称，获取对应测试数据

    请在parametrize装饰器中第二个参数传入测试数据Excel中的subtitle值列表，例如：['subtitle1','subtitle2']；此时，返回值为dict。

    当没有subtitle时，请传入['only1']或['all']或'*'；此时，返回值为list[dict]。
    """
    excel = get_test_excel
    global test_info
    sheet_name = test_info.get('class_name')
    method_name = test_info.get('method_name')
    excel.set_sheet_name(sheet_name)
    if request.param in ('only1', 'all', '*'):
        datas = excel.get_datas(method_name)
    else:
        datas = excel.get_datas_by_subtitle(method_name, request.param)
    return datas
